package ai.leinao.alarm.limit;

import ai.leinao.alarm.domain.AlarmInhibit;
import ai.leinao.alarm.domain.GroupAlarm;
import ai.leinao.alarm.domain.RawAlarm;
import ai.leinao.alarm.service.AlarmInhibitService;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * 告警抑制处理
 *
 * @author zhong.wang
 * @date 2025/9/3
 **/
@Component
@Slf4j
public class AlarmInhibitProcessor {

    /**
     * 告警静默处理
     */
    private final AlarmSilenceProcessor alarmSilenceProcessor;

    /**
     * 定期清理间隔
     */
    private static final long CHECK_INTERVAL = 60_000L;


    /**
     * 原告警抑制缓存
     */
    private final Map<Long, Map<String, SourceAlarmEntry>> sourceAlarmCache;

    /**
     * 默认源告警TTL为4小时
     */
    private static long SOURCE_ALERT_TTL = 4 * 60 * 60 * 1000L;


    /**
     * 告警抑制缓存
     */
    private final Map<Long, AlarmInhibit> inhibitRules;

    private final AlarmInhibitService alarmInhibitService;

    /**
     * 构造函数
     *
     * @param alarmSilenceProcessor
     * @param alarmInhibitService
     */
    public AlarmInhibitProcessor(AlarmSilenceProcessor alarmSilenceProcessor, AlarmInhibitService alarmInhibitService) {
        this.alarmSilenceProcessor = alarmSilenceProcessor;
        this.alarmInhibitService = alarmInhibitService;
        this.sourceAlarmCache = new ConcurrentHashMap<>(8);
        this.inhibitRules = new ConcurrentHashMap<>(8);
        initInhibitRules();
        startScheduledCleanupCache();
    }

    /**
     * 初始化抑制规则
     */
    private void initInhibitRules() {
        List<AlarmInhibit> alarmInhibits = alarmInhibitService.getAlarmInhibits();
        alarmInhibits.forEach(rule -> this.inhibitRules.put(rule.getId(), rule));
    }

    /**
     * 定时清理缓存
     */
    private void startScheduledCleanupCache() {
        ThreadFactory threadFactory = new ThreadFactoryBuilder()
                .setUncaughtExceptionHandler((thread, throwable) -> {
                    log.error("定时清理抑制缓存时发生未捕获异常。");
                    log.error(throwable.getMessage(), throwable);
                })
                .setDaemon(true)
                .setNamePrefix("inhibit-clean-up-%d")
                .build();
        ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(threadFactory);
        // 定时清除过期的源告警
        scheduledExecutor.scheduleAtFixedRate(() -> {
            try {
                sourceAlarmCache.values().forEach(this::cleanupExpiredEntries);
                // Remove empty rule caches
                sourceAlarmCache.entrySet().removeIf(entry -> entry.getValue().isEmpty());
            } catch (Exception e) {
                log.error("定时清理抑制缓存时发生错误", e);
            }
        }, CHECK_INTERVAL, CHECK_INTERVAL, TimeUnit.MILLISECONDS);
    }

    /**
     * 告警抑制处理
     * @param groupAlarm
     */
    public void inhibitAlarm(GroupAlarm groupAlarm) {
        //log.info("开始处理告警抑制逻辑 ...");
        if (groupAlarm == null) {
            log.warn("接收到空的组告警，跳过处理。");
            return;
        }
        try {
            // 如果没有抑制规则，则直接发送告警
            if (inhibitRules.isEmpty()) {
                alarmSilenceProcessor.silenceAlarm(groupAlarm);
                return;
            }

            // 处理每个单独的告警。如果和源标签匹配，则缓存源告警
            for (RawAlarm alert : groupAlarm.getAlarms()) {
                for (AlarmInhibit rule : inhibitRules.values()) {
                    if (isSourceAlert(alert, rule)) {
                        cacheSourceAlarm(alert, rule);
                    }
                }
            }

            // 过滤掉需要抑制的告警
            groupAlarm.getAlarms().removeIf(this::shouldInhibit);

            // 对剩余的告警进行沉默处理
            if (!groupAlarm.getAlarms().isEmpty()) {
                alarmSilenceProcessor.silenceAlarm(groupAlarm);
            }
        } catch (Exception e) {
            log.error("处理告警抑制时发生错误，组告警: {}", groupAlarm, e);
        }
    }


    /**
     * 检查告警是否与抑制规则的源标签匹配
     * @param alarm
     * @param rule
     * @return
     */
    private boolean isSourceAlert(RawAlarm alarm, AlarmInhibit rule) {
        if (Objects.isNull(alarm) || Objects.isNull(rule)) {
            log.warn("在 isSourceAlert 方法中接收到空的告警或规则");
            return false;
        }
        return matchLabels(alarm.getContext(), rule.getSourceLabels());
    }

    /**
     * 检查告警是否应被任何活跃的源告警所抑制
     * @param alarm 需要处理的单个告警
     */
    private boolean shouldInhibit(RawAlarm alarm) {
        if (Objects.isNull(alarm)) {
            log.warn("在 shouldInhibit 方法中接收到空的告警");
            return false;
        }

        for (AlarmInhibit rule : inhibitRules.values()) {
            if (!matchLabels(alarm.getContext(), rule.getTargetLabels())) {
                continue;
            }

            List<RawAlarm> sourceAlerts = getActiveSourceAlerts(rule);
            if (sourceAlerts.isEmpty()) {
                continue;
            }

            for (RawAlarm source : sourceAlerts) {
                if (matchEqualLabels(source, alarm, rule.getEqualLabels())) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 检查源告警和目标告警的相等标签是否具有相同的值
     * @param source 用于抑制其他告警的源告警
     * @param target 可能被抑制的目标告警
     * @param equalLabels 必须相等的标签列表
     * @return 如果所有相等标签的值都相同则返回true，否则返回false
     */
    private boolean matchEqualLabels(RawAlarm source, RawAlarm target, List<String> equalLabels) {
        if (source == null || target == null) {
            log.warn("在 matchEqualLabels 方法中接收到空的源告警或目标告警");
            return false;
        }
        if (equalLabels == null || equalLabels.isEmpty()) {
            return true;
        }
        Map<String, String> sourceLabels = source.getContext();
        Map<String, String> targetLabels = target.getContext();

        return equalLabels.stream().allMatch(label -> {
            String sourceValue = sourceLabels.get(label);
            String targetValue = targetLabels.get(label);
            return sourceValue != null && sourceValue.equals(targetValue);
        });
    }

    /**
     * 获取抑制规则的活跃源告警
     * @param rule 抑制规则
     * @return 活跃的源告警列表
     */
    private List<RawAlarm> getActiveSourceAlerts(AlarmInhibit rule) {
        if (rule == null) {
            log.warn("在 getActiveSourceAlerts 方法中接收到空的规则");
            return Collections.emptyList();
        }
        Map<String, SourceAlarmEntry> ruleCache = sourceAlarmCache.get(rule.getId());
        if (ruleCache == null || ruleCache.isEmpty()) {
            return Collections.emptyList();
        }

        long now = System.currentTimeMillis();
        return ruleCache.values().stream()
                .filter(entry -> entry.getExpiryTime() > now)
                .map(SourceAlarmEntry::getAlarm)
                .collect(Collectors.toList());
    }


    /**
     * 检查告警标签是否与所需标签匹配
     * @param alarmLabels
     * @param requiredLabels
     * @return
     */
    private boolean matchLabels(Map<String, String> alarmLabels, Map<String, String> requiredLabels) {
        if (alarmLabels == null || requiredLabels == null) {
            log.warn("在 matchLabels 方法中接收到空的告警标签或必需标签");
            return false;
        }
        return requiredLabels.entrySet().stream()
                .allMatch(entry -> entry.getValue().equals(alarmLabels.get(entry.getKey())));
    }

    /**
     * 缓存源告警用于抑制规则
     * @param alarm 需要处理的单个告警
     * @param rule 抑制规则
     */
    private void cacheSourceAlarm(RawAlarm alarm, AlarmInhibit rule) {
        if (Objects.isNull(alarm) || Objects.isNull(rule)) {
            log.warn("在 cacheSourceAlarm 方法中接收到空的告警或规则");
            return;
        }
        Map<String, SourceAlarmEntry> ruleCache = sourceAlarmCache.computeIfAbsent(
                rule.getId(),
                k -> new ConcurrentHashMap<>()
        );

        SourceAlarmEntry entry = new SourceAlarmEntry(
                alarm,
                System.currentTimeMillis(),
                System.currentTimeMillis() + SOURCE_ALERT_TTL
        );
        ruleCache.put(alarm.getFingerprint(), entry);
        cleanupExpiredEntries(ruleCache);
    }

    /**
     * 从缓存中移除过期的条目
     * @param cache 源告警缓存条目映射
     */
    private void cleanupExpiredEntries(Map<String, SourceAlarmEntry> cache) {
        if (cache == null) {
            log.warn("在 cleanupExpiredEntries 方法中接收到空的缓存");
            return;
        }
        long now = System.currentTimeMillis();
        cache.entrySet().removeIf(entry -> entry.getValue().getExpiryTime() <= now);
    }

    /**
     * 原告警告缓存条目
     */
    @Data
    @AllArgsConstructor
    private static class SourceAlarmEntry {
        /**
         * 告警信息
         */
        private final RawAlarm alarm;
        /**
         * 创建时间
         */
        private final long createTime;
        /**
         * 过期时间
         */
        private final long expiryTime;
    }

}
